// Copyright (C) 2022 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
//
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<广西木子科技有限公司>，授权编号：<NT220402151538>，代码指纹：<A.648883738.885>，技术对接人微信：<ID: Lingmuziyi>】
// 【授权寄送：<收件：李先生、地址：南宁市科园西十路11号国电智能大厦1101F、电话：17736659550、邮箱：yingshashou@vip.qq.com>】
//
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
//
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.eva.framework.httpfile;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.eva.framework.utils.LoggerFactory;

/**
 * 使用Apache的fileupload库实现的Http文件上传实用类（适用于各种富客户端上传文件，包括Andriod、iOS设备等）.
 * <p>
 * <b>注意：</b>本类中的额外数据字段是通过额外文件数据上传上来的（而非通过URL后面带的参数的方式），
 * 虽然通过上传URL后面带参数的方式给服务端最为简单，但在有些客户端库（如iOS的AFNetworking3.0库）使用时，
 * 会出现卡住的bug（这是iOS的AFNetworking3.0库的bug），所以为了通用性，本类一律不使用URL带参数的方式。
 *
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 * @revision 1.1, 20170306 为了规范化多平台上传文件时的额外参数上传，增加了processFileUpload2方法
 */
public class FileUpload {
    public final static String SUCESS_KEY_NAME = "sucess";

    /**
     * 文件上传处理实现方法。
     *
     * @param request
     * @param response
     * @param saveToDir        文件将要保存到的目录
     * @param THRESHOLD_SIZE   磁盘临时缓存文件阀值。
     *                         <pre>
     *                                 Apache文件上传组件在解析上传数据中的每个字段内容时，需要临时保存解析出的数据，以
     *                             便在后面进行数据的进一步处理（保存在磁盘特定位置或插入数据库）。因为Java虚拟机默
     *                             认可以使用的内存空间是有限的，超出限制时将会抛出“java.lang.OutOfMemoryError”错
     *                             误。如果上传的文件很大，例如800M的文件，在内存中将无法临时保存该文件内容，Apache
     *                             文件上传组件转而采用临时文件来保存这些数据；但如果上传的文件很小，例如600个字节的
     *                             文件，显然将其直接保存在内存中性能会更加好些。
     *
     *                                 setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值（以
     *                             字节为单位的int值），如果从没有调用该方法设置此临界值，将会采用系统默认值10KB。对
     *                             应的getSizeThreshold() 方法用来获取此临界值。
     *                         </pre>
     * @param MAX_FILE_SIZE    单个文件最大大小
     * @param MAX_REQUEST_SIZE 所有文件总大小（因为本方法支持多文件上传）
     * @return 从Multipart Content中取出客户端传过来的参数
     * @throws Exception
     */
    public static HashMap<String, Object> processFileUpload2(HttpServletRequest request
            , HttpServletResponse response, String saveToDir
            , int THRESHOLD_SIZE, int MAX_FILE_SIZE, int MAX_REQUEST_SIZE) throws Exception {
        // 客户端上传文件时额外上来的参数存放于此map中
        // * 因http 文件上传的特殊性，没有办法在单独的方法里提取出额外的参数，所以只
        // * 能使用此方法在所有数据上传完成后才能保证完整拿到上传的额外参数
        HashMap<String, Object> parametersFromClient = new HashMap<String, Object>();

        // sucess参数为true表示本次文件上传处理成功，否则表示处理失败
        parametersFromClient.put(SUCESS_KEY_NAME, Boolean.FALSE);

//		boolean sucess = false;

        if (saveToDir == null)
            throw new Exception("[HTTP文件上传] saveToDir == null!");

        // checks if the request actually contains upload file
        if (!ServletFileUpload.isMultipartContent(request)) {
//			PrintWriter writer = response.getWriter();
//			writer.println("Request does not contain upload data");
//			writer.flush();
//			return;
            LoggerFactory.getLog().error("[HTTP文件上传] 没有需要上传的文件，ServletFileUpload.isMultipartContent(request)!=true!");
            throw new IOException("[HTTP文件上传] Not file to upload!");
        }

        // configures upload settings
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(THRESHOLD_SIZE);
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setFileSizeMax(MAX_FILE_SIZE);
        upload.setSizeMax(MAX_REQUEST_SIZE);

        // constructs the directory path to store upload file
        String uploadPath = saveToDir;//getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
        // creates the directory if it does not exist
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }

        try {
            // parses the request's content to extract file data
            List formItems = upload.parseRequest(request);
            Iterator iter = formItems.iterator();

            // 遍历获取除文件数据本身以外的额外参数（相关原理，请见Apache的开源fileupload组件官方文档）
            // iterates over form's fields
            while (iter.hasNext()) {
                FileItem item = (FileItem) iter.next();
                // processes only fields that are not form fields
                if (!item.isFormField()) {
                    String fileName = new File(item.getName()).getName();
                    String filePath = uploadPath + File.separator + fileName;
                    File storeFile = new File(filePath);

                    // saves the file on disk
                    item.write(storeFile);
                }
                // 上传的额外参数
                else {
                    String fieldName = item.getFieldName();
                    String fieldValue = item.getString();

                    LoggerFactory.getLog().debug("[HTTP文件上传] 已读取到额外参数：fieldName="
                            + fieldName + ", fieldValue=" + fieldValue);

                    // 把参数放入返回数据的map中
                    parametersFromClient.put(fieldName, fieldValue);
                }
            }
//			request.setAttribute("message", "Upload has been done successfully!");

//			sucess = true;
            parametersFromClient.put(SUCESS_KEY_NAME, Boolean.TRUE);
        } catch (Exception ex) {
//			ex.printStackTrace();
            LoggerFactory.getLog().error("[HTTP文件上传] 处理文件上传时出错了，"
                    + ex.getMessage(), ex);
            throw ex;
//			request.setAttribute("message", "There was an error: " + ex.getMessage());
        }

        return parametersFromClient;
    }
}
